#include "lib_uart.h"

//#define UART_BAUD_RATE          9600
//#define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) \ ( ( F_OSC ) / ( ( UART_BAUD_RATE ) * 16UL ) - 1 )

//#define UART_UCSRB  UCSR0B

volatile	static	BOOL		SerialEvent							= FALSE;

volatile	static eMBRcvState	SerialRxState;
volatile	static	eMBSndState SerialTxState;

volatile			UCHAR		SerialBuf[MB_SIZE_MAX];
volatile	static	USHORT		SerialRxBufPos;
volatile	static	UCHAR		*SerialTxBufPos;
volatile	static	USHORT		SerialTxBytes2End;
volatile	static	BOOL		SerialFrameReceived = FALSE;

void SerialEnable( BOOL xRxEnable, BOOL xTxEnable ) {
	CRITICAL_SECTION_START;
	if( xRxEnable ) {
		TimerModbus35=TimerModbus35Orig;
		TimerModbus35_counter=TimerModbus35Orig;
		SerialRxState=STATE_RX_INIT;
        UCSRB |= (BV(RXEN) | BV(RXCIE));	//      разрешаю прием, разрешаю прерывание
    } else {
        TimerModbus35=FALSE;
        SerialRxState = STATE_RX_IDLE;
        UCSRB &= ~(BV(RXEN) | BV(RXCIE));   //      запрещаю прием, запрещаю прерывание
    }
    if( xTxEnable ) {
    	SerialTxState=STATE_TX_XMIT;
    	UCSRB |= BV(TXEN) |  BV(UDRIE);		//		разрешаем передачу и НЕТ прерывание при опустошении
    } else {
        UCSRB &= ~(BV(TXEN) | BV(UDRIE));
        SerialTxState = STATE_TX_IDLE;
    }
    CRITICAL_SECTION_END;
}

void SerialInit(ULONG ulBaudRate, eMBParity eParity ) {
	CRITICAL_SECTION_START;
	SerialEnable( FALSE, FALSE );
	UCHAR ucUCSRC = 0;

	/* Set baud rate */
	//UBRRH = (unsigned char)(ubrr>>8);
	//UBRRL = (unsigned char)ubrr;

	//UBRR = UART_BAUD_CALC( ulBaudRate, F_CPU );
    UBRR=47;    //      9600 для 7.3728 Mhz

    /* Set frame format: 8data, 2stop bit */
    //UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
    ucUCSRC |= (BV(UCSZ0) | BV(UCSZ1));		//	data 8 bit

    switch ( eParity ) {
        case MB_PAR_EVEN:
            ucUCSRC |= BV(UPM1);
            break;
        case MB_PAR_ODD:
        	ucUCSRC |= (BV(UPM1)|BV(UPM0));
            break;
        case MB_PAR_NONE:
            break;
    }

#if defined (__AVR_ATmega168__)
    UCSRC |= ucUCSRC;
#elif defined (__AVR_ATmega88__)
    UCSRC |= ucUCSRC;
#elif defined (__AVR_ATmega88A__)
    UCSRC |= ucUCSRC;
#elif defined (__AVR_ATmega169__)
    UCSRC |= ucUCSRC;
#elif defined (__AVR_ATmega8__)
    UCSRC = _BV( URSEL ) | ucUCSRC;
#elif defined (__AVR_ATmega16__)
    UCSRC = _BV( URSEL ) | ucUCSRC;
#elif defined (__AVR_ATmega32__)
    UCSRC = _BV( URSEL ) | ucUCSRC;
#elif defined (__AVR_ATmega128__)
    UCSRC |= ucUCSRC;
#endif

//    ULONG           usTimerT35_50us;
/* If baudrate > 19200 then we should use the fixed timer values
 * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
 */
//       if( ulBaudRate > 19200 ) {
//            usTimerT35_50us = 35;       /* 1800us. */
//        } else {
            /* The timer reload value for a character is given by:
             *
             * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
             *             = 11 * Ticks_per_1s / Baudrate
             *             = 220000 / Baudrate
             * The reload for t3.5 is 1.5 times this value and similary
             * for t3.5.
             */
//            usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
//        }

    //TimerModbusSet(usTimerT35_50us);
    TimerModbusSet(25000);
    SerialRxState = STATE_RX_IDLE;
    SerialTxState = STATE_TX_IDLE;

    CRITICAL_SECTION_END;
}

//void SerialFlush ( void ) {
//	u08 dummy;
//	while ( UCSRA & (1<<RXC) ) dummy = UDR;
//}

//void SerialPutByte( u08 ucByte ) {
//	while ( !( UCSRA & BV(UDRE)) );
//	UDR = ucByte;
//}

//void SerialGetByte( u08 * pucByte ) {
//    *pucByte = UDR;
//}


eMBErrorCode SerialReciveFrame ( void ) {
    eMBErrorCode    eStatus = MB_ENOERR;

    CRITICAL_SECTION_START;
    // Length and CRC check
    if( ( SerialRxBufPos >= MB_SIZE_MIN ) && ( usMBCRC16( ( UCHAR * ) SerialBuf, SerialRxBufPos ) == 0 ) ) {
        // Save the address field. All frames are passed to the upper layed and the decision if a frame is used is done there.
        //*pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF];
        // Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus size of address field and CRC checksum.
        //*pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );
        // Return the start of the Modbus PDU to the caller.
        //*pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];
        SerialFrameReceived = TRUE;
    } else {
        eStatus = MB_EIO;
    }
    CRITICAL_SECTION_END;
    return eStatus;
}

eMBErrorCode SerialSendFrame (const UCHAR SlaveAddress, const UCHAR MBFunc, const UCHAR * Frame, const USHORT FrameLenght ) {
	eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          usCRC16;
    CRITICAL_SECTION_START;
    //LED_OFF;

    //if ( (SerialRxState == STATE_RX_IDLE) && (SerialTxState == STATE_TX_IDLE)) {
    if (SerialRxState == STATE_RX_IDLE) {

    	SerialBuf[MB_ADDR] = SlaveAddress;
    	SerialBuf[MB_FUNC]= MBFunc;
    	SerialTxBytes2End = 2;

    	memcpy( (UCHAR*)&SerialBuf[MB_DATA], Frame, FrameLenght );
    	SerialTxBytes2End += FrameLenght;

        usCRC16 = usMBCRC16( ( UCHAR * ) SerialBuf, SerialTxBytes2End );	// Calculate CRC16 checksum for Modbus-Serial-Line-PDU.
        SerialBuf[SerialTxBytes2End++] = ( UCHAR )( usCRC16 & 0xFF );
        SerialBuf[SerialTxBytes2End++] = ( UCHAR )( usCRC16 >> 8 );
        SerialTxBufPos=SerialBuf;
        SerialEnable( FALSE, TRUE );										// Activate the transmitter.
    } else {
    	LED_ON;
        eStatus = MB_EIO;
    }
    CRITICAL_SECTION_END;
    return eStatus;
}

ISR (USART_UDRE_vect) {								//	int	12
    //CRITICAL_SECTION_START;
    switch ( SerialTxState ) {
    case STATE_TX_IDLE:								// We should not get a transmitter event if the transmitter is in idle state.
        SerialEnable (TRUE,FALSE);					// enable receiver/disable transmitter.
        break;
    case STATE_TX_XMIT:
        if( SerialTxBytes2End != 0 ) {				// check if we are finished.
        	UDR=*SerialTxBufPos;
        	SerialTxBufPos++;						// next byte in sendbuffer.
            SerialTxBytes2End--;
        } else {
        	SerialEvent = TRUE;
            EventPost( EV_FRAME_SENT );
            SerialEnable( TRUE, FALSE );			// Disable transmitter. This prevents another transmit buffer empty interrupt.
        }
        break;
    }
    //CRITICAL_SECTION_END;
}

ISR (USART_RXC_vect) {									//	int	11
	volatile register u08 c;
	//CRITICAL_SECTION_START;
	c=UDR;
	switch ( SerialRxState ) {
	case STATE_RX_INIT:									// If we have received a character in the init state we have to wait until the frame is finished.
		TimerModbus35_counter=TimerModbus35;
		break;
	case STATE_RX_ERROR:								// In the error state we wait until all characters in the damaged frame are transmitted.
		TimerModbus35_counter=TimerModbus35;
	    break;
	case STATE_RX_IDLE:									// In the idle state we wait for a new character.
		SerialRxBufPos = 0;								// If a character is received the t1.5 and t3.5 timers are started and the receiver is in the state STATE_RX_RECEIVCE.
		SerialFrameReceived = FALSE;
		SerialBuf[SerialRxBufPos++] = c;
	    SerialRxState = STATE_RX_RCV;
	    TimerModbus35_counter=TimerModbus35;			// Enable t3.5 timers.
	    break;
	case STATE_RX_RCV:									//	We are currently receiving a frame. Reset the timer after every character received.
		if( SerialRxBufPos < MB_SIZE_MAX ) {	//	If more than the maximum possible number of bytes in a modbus frame is received the frame is ignored.
			SerialBuf[SerialRxBufPos++] = c;
	    } else {
	    	SerialRxState = STATE_RX_ERROR;
	    }
		TimerModbus35_counter=TimerModbus35;
	    break;
	}
	//SerialSendFrame ( (CHAR*)&SerialRxBuf[0], 1 );
	//CRITICAL_SECTION_END;
}



void SerialTimer35Expired( void ) {
    switch ( SerialRxState ) {
    case STATE_RX_INIT:			// Timer t35 expired. Startup phase is finished.
        SerialEvent = TRUE;
        EventPost( EV_READY );
        break;
    case STATE_RX_RCV:			// A frame was received and t35 expired. Notify the listener that a new frame was received.
        SerialEvent = TRUE;
        EventPost( EV_FRAME_RECEIVED );
        break;
    case STATE_RX_ERROR:		// An error occured while receiving the frame.
        break;
    default:					// Function called in an illegal state.
    	break;
        //assert( ( eRcvState == STATE_RX_INIT ) || ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) );
    }
    TimerModbus35=FALSE;
    SerialRxState = STATE_RX_IDLE;
}
